package com.gome.transactiontree.repository;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import com.gome.transactiontree.api.Transaction;
import com.gome.transactiontree.api.TransactionCallback;
import com.gome.transactiontree.exception.TransactionIOException;
import com.gome.transactiontree.logging.TransactionLogger;
import com.gome.transactiontree.serializer.KryoThreadLocalSerializer;
import com.gome.transactiontree.serializer.ObjectSerializer;

public class FileSystemTransactionRepository  implements TransactionStore{

    private String rootPath = "/txtreedb";

    private ObjectSerializer<Transaction> serializer = new KryoThreadLocalSerializer<Transaction>();

    public void setRootPath(String rootPath) {
        this.rootPath = rootPath;
    }
    

	@Override
	public Transaction get(String transactionId) {
		String fullFileName = getFullFileName(transactionId);
        File file = new File(fullFileName);

        if (file.exists()) {
            return readTransaction(file);
        }

        return null;
	}

	@Override
	public void put(String transactionId, Transaction transaction) {
        String file = getFullFileName(transactionId);

        FileChannel channel = null;
        RandomAccessFile raf = null;

        byte[] content = serializer.serialize(transaction);
        try {
            raf = new RandomAccessFile(file, "rw");
            channel = raf.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(content.length);
            buffer.put(content);
            buffer.flip();

            while (buffer.hasRemaining()) {
                channel.write(buffer);
            }

            channel.force(true);
        } catch (Exception e) {
            throw new TransactionIOException(e);
        } finally {
        	if(raf != null){
        		try {
        			raf.close();
                } catch (IOException e) {
                    throw new TransactionIOException(e);
                }
        	}
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    throw new TransactionIOException(e);
                }
            }
        }
		
	}

	@Override
	public void delete(String transactionId) {
		String fullFileName = getFullFileName(transactionId);
        File file = new File(fullFileName);
        if (file.exists()) {
            file.delete();
        }
		
	}

	@Override
	public void foreach(TransactionCallback<Object> action) {
        File path = new File(rootPath);
        File[] files = path.listFiles();

        for (File file : files) {
        	Transaction transaction = readTransaction(file);
			try {
				action.doInTransaction(transaction);
			} catch (Throwable e) {
				throw new IllegalStateException(e.getMessage(), e);
			}
        }
	}

    private String getFullFileName(String transactionId) {
        return String.format("%s/%s", rootPath, transactionId);
    }
    
    private Transaction readTransaction(File file) {

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);

            byte[] content = new byte[(int) file.length()];

            fis.read(content);

            if (content != null) {
                return serializer.deserialize(content);
            }
        } catch (Exception e) {
            throw new TransactionIOException(e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    throw new TransactionIOException(e);
                }
            }
        }
		return null;
    }

	@Override
	public void destroy() throws Exception {
		TransactionLogger.REPOSITORY.info("已关闭文件存储");
	}


	@Override
	public void afterPropertiesSet() throws Exception {
		File rootPathFile = new File(rootPath);
        if (!rootPathFile.exists()) {
            boolean result = rootPathFile.mkdir();

            if (!result) {
                throw new TransactionIOException("不能创建根路径:" + rootPath);
            }
        }
        TransactionLogger.REPOSITORY.info("初始化文件存储成功，数据目录 {}", new File(rootPath).getAbsolutePath());
	}
}
